home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_100
/
188_01
/
trans.doc
< prev
next >
Wrap
Text File
|
1987-09-29
|
14KB
|
275 lines
.mt 0
.mb 11
.po 13
/*
HEADER: ;
TITLE: C elementary transcendentals;
VERSION: 1.0;
DESCRIPTION: Source code for all standard C
transcendentals
Employs ldexp() and frexp() functions; if
suitable versions of these are not provided
by a given compiler, the versions provided in
source code will require adaptation to the
double float formats of the compiler.
KEYWORDS: Transcendentals, library;
SYSTEM: CP/M-80, V3.1;
FILENAME: TRANS.C;
WARNINGS: frexp() and ldexp() are implementation
dependent. The compiler employed does not
support minus (-) unary operators in
initializer lists, which are required by the
code.
AUTHORS: Tim Prince;
COMPILERS: MIX C, v2.0.1;
*/
Transcendental Function Algorithms
All programming languages which are descended from FOR-
TRAN or Algol include pre-defined transcendental functions.
Standard textbooks on programming assume that the usual math
functions are already available and not worth the paper re-
quired to describe how they might be coded. This is unfor-
tunate, as few computer systems provide reliable transcenden-
tals, and a basic understanding of their contents is helpful
in their use.
The following examples use polynomial representations,
because they lead to compact code and give average accuracy
within one bit of the best available. Faster algorithms exist
but require more code or data storage, if written in the same
language. Commercial versions of the functions are usually
written in assembler, but the ideas are best expressed in
higher level languages. Obscure tricks which an anonymous
coder thought would improve speed are useless when the program
fails and the source code is not available. These functions
have been tested in nearly the same form on several computers
in both FORTRAN and C.
The author's hope is that these examples may set a mini-
mum standard for accuracy and speed of C library transcenden-
tals. Failing this, the individual user should have the
opportunity to check whether any deficiencies of his code may
be due to the library provided with the C compiler. C's soon
to be standardized support of bit fields and low level opera-
tions on doubles helps in the expression of these algorithms.
Since C, even in the future ANSI standard, will not support
pure single precision on computers which were not designed for
mixed precision arithmetic, and the standard library functions
are double, we show only the double precision, with the under-
standing that changes in the polynomials would adapt to other
precisions. The file TRANSLIB.FOR shows FORTRAN versions in
single precision, which should serve as a demonstration of the
changes required to vary precision and language.
The polynomial coefficients were determined by running
one of Steve Ruzinsky's programs (1). In each case, the
polynomial is derived by truncating a standard Taylor series
or rational polynomial representation of the function and
using Steve's program to obtain more accuracy with fewer terms
in the required range. In some cases, 25 digit arithmetic is
required to fit a polynomial accurate to at least 16 digits.
Tables of coefficients have been generated for polynomials of
any accuracy required up to 20 or more significant digits.
Trigonometric functions can be calculated with adequate
efficiency using portable code, even in archaic dialects of
FORTRAN, Pascal, and C. When using polynomials, it appears
best to use the sin() function as the basic series and calcu-
late the others from it. A polynomial for tan() requires as
many terms as sin() and cos() together. Since cos() and tan()
may be calculated from sin(), only one shorter table of coef-
ficients is needed. In effect, tan() is calculated by a
rational polynomial approximation. Expressing these trigono-
metric relationships by #define teaches the preprocessor alge-
bra which an optimizing compiler could use to advantage.
Check that your compiler does not execute functions multiple
times as a result of macro expansion. Tan() could be calcu-
lated much faster with a big table as is built in to the 8087,
but most programs we have seen spend more time on sin() and
cos().
The first step in calculating sin() is to reduce the
range to |x| < pi/2 by subtracting the nearest multiple of pi.
The ODD() function shown assumes that (int) will extract the
low order bits in case the argument exceeds maxint. The
multiplication and subtraction should be done in long double
if it is available, but in any case the range reduction should
not reduce accuracy when the original argument was already in
the desired range. The method most often used saves one
divide (which may be changed to a multiply), at the cost of an
unnecessary roundoff. Worse, the result may underflow to
zero. Underflows in the code shown occur only where a zero
does not affect the result. With the precautions taken, extra
precision is not needed anywhere other than the range
reduction.
Most computer systems include a polynomial evaluation
function, sometimes in microcode. To save space we prefer to
use such a function even though it may be slower than writing
out the polynomial in Horner form. Such functions operate
like poly() shown here, but (presumably) faster. This modu-
larity permits special hardware features to be used more
effectively, although the time required to call another func-
tion may be excessive. Loop unrolling (shown here) or odd and
even summing (see exp2()) are often effective. The series
used for elementary functions have terms increasing in magni-
tude fast enough so that there is no need for extra precision
intermediate computation.
The arctan function uses several substitutions to reduce
to a range of 0 < x < 1. Although the Taylor series does not
converge over this range, the minimax polynomial does, but so
many terms are needed that one further step of range reduction
is preferred. Code length may be traded for speed by using a
table of tangents with a shorter series. A rational polyno-
mial(3) is used by many run-time libraries, at some cost of
accuracy. A series with one less term would still give over
15 digits accuracy, which is the maximum possible on some
systems.
Although log base 2 is not usually included in the list
of library functions, it is almost always used as the basic
logarithm for binary floating point, because it is the most
efficient base for exponentiation with a float power. Many
implementations fail to obtain the logarithm of numbers close
to 1. The following routine is the most widely tested of the
examples given here, and has been found accurate on all ma-
chines which subtract 1 accurately (some don't!). In theory,
precision is lost when the exponent is added in at the end,
but this is not serious in the most common cases of numbers
near 1. Making the function long double would correct this.
Log2 can be written in portable code if the standard
functions frexp() and ldexp() for extracting and changing the
exponent field of a floating point number are available. On
many systems, adequate speed will not be obtained unless the
code is supplied in line. This should be possible using
unions of structures, but direct use of any applicable float-
ing point hardware ins